#pragma once
#include <iostream>
#include <semaphore.h>
#include <vector>
#include <pthread.h>
#define NUM 5

template <class T>
class RingQueue
{
public:
    // 构造
    RingQueue(int num = NUM) : _q(num), _cap(num), _p_step(0), _c_step(0)
    {
        // 初始化信号量
        sem_init(&_pace_sem, 0, num); // 空间信号量起初为满
        sem_init(&_data_sem, 0, 0);   // 数据信号量起初为空
        //初始化锁
        pthread_mutex_init(&_p_mutex,nullptr);
        pthread_mutex_init(&_c_mutex,nullptr);
    }

    // void P(sem_t *sem)
    void P(sem_t& sem)
    {
        sem_wait(&sem);
    }

    // void V(sem_t *sem)
     void V(sem_t& sem)
    {
        sem_post(&sem);
    }
    void Lock(pthread_mutex_t& mutex)
    {
       pthread_mutex_lock(&mutex);
    }
    void Unlock(pthread_mutex_t& mutex)
    {
       pthread_mutex_unlock(&mutex);
    }
    // 生产
    void push(T &in)
    {
        // 生产之前，先申请空间信号量
        // sem_wait(&_pace_sem); //P()
        Lock(_p_mutex);  //加锁
        P(_pace_sem);
        // 资源申请成功，进行生产，具体使用哪一个空间，需要我们自己去定，所以我们还需要定义空间下标
        _q[_p_step++] = in;
        _p_step %= _cap; //_p_step一直加加不好，怕数据溢出
        // 提醒消费者进行消费-->数据信号量加1
        // sem_post(&_data_sem); //V()
        V(_data_sem);
        Unlock(_p_mutex); //解锁
    }
    // 消费
    void pop(T *out)
    {
        // 消费之前，先申请数据信号量
        // sem_wait(&_data_sem); //P()
        Lock(_c_mutex);
        P(_data_sem);
        *out = _q[_c_step++];
        _c_step %= _cap; // 防止数据溢出
        // 提醒生产者进行生产-->空间信号量加1
        // sem_post(&_pace_sem);
        V(_pace_sem);
        Unlock(_c_mutex);
    }

    // 析构
    ~RingQueue()
    {
        // 销毁
        sem_destroy(&_pace_sem);
        sem_destroy(&_data_sem);
        pthread_mutex_destroy(&_p_mutex);
        pthread_mutex_destroy(&_c_mutex);
    }

private:
    std::vector<T> _q; // 缓冲区
    int _cap;            // 缓冲区的大小
    sem_t _pace_sem;     // 生产空间信号量
    sem_t _data_sem;     // 数据信号量
    int _p_step;         // 生产的位置
    int _c_step;         // 消费的位置
    pthread_mutex_t _p_mutex;  //生产者之间的锁
    pthread_mutex_t _c_mutex;  //消费者之间的锁
};